/*
    GNU GENERAL LICENSE
    Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2016 Lobo Evolution

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public
    License as published by the Free Software Foundation; either
    verion 3 of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General License for more details.

    You should have received a copy of the GNU General Public
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    

    Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
 */
/*
 * Created on Apr 17, 2005
 */
package org.lobobrowser.html.renderer;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

import org.lobobrowser.html.dombl.ModelNode;
import org.lobobrowser.html.renderstate.RenderState;

/**
 * The Class RWord.
 */
public final class RWord extends BaseBoundableRenderable {

    /** The shown word. */
    private String shownWord;

    /** The font metrics. */
   private FontMetrics fontMetrics;

    /** The descent. */
   private int descent;

    /** The ascent plus leading. */
   private int ascentPlusLeading;

    /**
     * Instantiates a new r word.
     *
     * @param me
     *            the me
     * @param word
     *            the word
     * @param container
     *            the container
     * @param fontMetrics
     *            the font metrics
     * @param descent
     *            the descent
     * @param ascentPlusLeading
     *            the ascent plus leading
     * @param height
     *            the height
     * @param textTransform
     *            the text transform
     */
    public RWord(ModelNode me, String word, RenderableContainer container,
            FontMetrics fontMetrics, int descent, int ascentPlusLeading,
            int height, int textTransform) {
        super(container, me);
        String renderedWord = textTransform == RenderState.TEXTTRANSFORM_NONE ? word
                : transformText(word, textTransform);
        this.shownWord = renderedWord;
        this.fontMetrics = fontMetrics;
        this.descent = descent;
        this.ascentPlusLeading = ascentPlusLeading;
        this.height = height;
        // TODO: In anti-aliasing, stringWidth is said not to be reliable.
        // Dimensions set when constructed.
        this.width = fontMetrics.stringWidth(renderedWord);
    }

    /**
     * Transform text.
     *
     * @param word
     *            the word
     * @param textTransform
     *            the text transform
     * @return the string
     */
    private String transformText(String word, int textTransform) {
        String string;
        switch (textTransform) {
        case RenderState.TEXTTRANSFORM_CAPITALIZE:
            string = Character.toTitleCase(word.charAt(0))
                    + word.substring(1).toLowerCase();
            break;
        case RenderState.TEXTTRANSFORM_LOWERCASE:
            string = word.toLowerCase();
            break;
        case RenderState.TEXTTRANSFORM_UPPERCASE:
            string = word.toUpperCase();
            break;
        default:
            string = word;
        }
        return string;
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BaseBoundableRenderable#invalidateLayoutLocal()
 */
    @Override
    protected void invalidateLayoutLocal() {
    }

    /*
 * (non-Javadoc)
 * @see net.sourceforge.xamj.domimpl.markup.Renderable#paint(java.awt.Graphics)
 */
    @Override
    public void paint(Graphics g) {
        RenderState rs = this.modelNode.getRenderState();
        String word = this.shownWord;
        int width = this.width;
        int ascentPlusLeading = this.ascentPlusLeading;
        int height = this.height;
        int textDecoration = rs.getTextDecorationMask();
        Color bkg = rs.getTextBackgroundColor();
        if (bkg != null) {
            Color oldColor = g.getColor();
            try {
                g.setColor(bkg);
                g.fillRect(0, 0, width, height);
            } finally {
                g.setColor(oldColor);
            }
        }
        g.drawString(word, 0, ascentPlusLeading);
        int td = textDecoration;
        if (td != 0) {
            if ((td & RenderState.MASK_TEXTDECORATION_UNDERLINE) != 0) {
                int lineOffset = ascentPlusLeading + 2;
                g.drawLine(0, lineOffset, width, lineOffset);
            }
            if ((td & RenderState.MASK_TEXTDECORATION_LINE_THROUGH) != 0) {
                FontMetrics fm = this.fontMetrics;
                int lineOffset = fm.getLeading()
                        + ((fm.getAscent() + fm.getDescent()) / 2);
                g.drawLine(0, lineOffset, width, lineOffset);
            }
            if ((td & RenderState.MASK_TEXTDECORATION_OVERLINE) != 0) {
                FontMetrics fm = this.fontMetrics;
                int lineOffset = fm.getLeading();
                g.drawLine(0, lineOffset, width, lineOffset);
            }
            if ((td & RenderState.MASK_TEXTDECORATION_BLINK) != 0) {
                // TODO
            }
        }
        Color over = rs.getOverlayColor();
        if (over != null) {
            Color oldColor = g.getColor();
            try {
                g.setColor(over);
                g.fillRect(0, 0, width, height);
            } finally {
                g.setColor(oldColor);
            }
        }
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#paintSelection(java.awt
 * .Graphics, boolean, org.lobobrowser.html.renderer.RenderableSpot,
 * org.lobobrowser.html.renderer.RenderableSpot)
 */
    @Override
    public boolean paintSelection(Graphics g, boolean inSelection,
            RenderableSpot startPoint, RenderableSpot endPoint) {
        int startX = -1;
        int endX = -1;
        if (this == startPoint.getRenderable()) {
            startX = startPoint.getX();
        }
        if (this == endPoint.getRenderable()) {
            endX = endPoint.getX();
        }
        if (!inSelection && (startX == -1) && (endX == -1)) {
            return false;
        }
        if ((startX != -1) && (endX != -1)) {
            if (endX < startX) {
                int temp = startX;
                startX = endX;
                endX = temp;
            }
        } else if ((startX != -1) && (endX == -1) && inSelection) {
            endX = startX;
            startX = -1;
        } else if ((startX == -1) && (endX != -1) && !inSelection) {
            startX = endX;
            endX = -1;
        }
        int width1 = -1;
        int width2 = -1;
        char[] wordChars = this.shownWord.toCharArray();
        if (startX != -1) {
            width1 = 0;
            FontMetrics fm = this.fontMetrics;
            for (int len = 0; len < wordChars.length; len++) {
                int w = fm.charsWidth(wordChars, 0, len);
                if (w > startX) {
                    break;
                }
                width1 = w;
            }
        }
        if (endX != -1) {
            width2 = 0;
            FontMetrics fm = this.fontMetrics;
            for (int len = 0; len < wordChars.length; len++) {
                int w = fm.charsWidth(wordChars, 0, len);
                if (w > endX) {
                    break;
                }
                width2 = w;
            }
        }
        if ((width1 != -1) || (width2 != -1)) {
            int startPaint = width1 == -1 ? 0 : width1;
            int endPaint = width2 == -1 ? this.width : width2;
            g.setColor(SELECTION_COLOR);
            g.setXORMode(SELECTION_XOR);
            g.fillRect(startPaint, 0, endPaint - startPaint, this.height);
            g.setPaintMode();
            return (width2 == -1);
        } else {
            if (inSelection) {
                g.setColor(SELECTION_COLOR);
                g.setXORMode(SELECTION_XOR);
                g.fillRect(0, 0, this.width, this.height);
                g.setPaintMode();
            }
            return inSelection;
        }
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#extractSelectionText(java
 * .lang.StringBuffer, boolean, org.lobobrowser.html.renderer.RenderableSpot,
 * org.lobobrowser.html.renderer.RenderableSpot)
 */
    @Override
    public boolean extractSelectionText(StringBuffer buffer,
            boolean inSelection, RenderableSpot startPoint,
            RenderableSpot endPoint) {
        int startX = -1;
        int endX = -1;
        if (this == startPoint.getRenderable()) {
            startX = startPoint.getX();
        }
        if (this == endPoint.getRenderable()) {
            endX = endPoint.getX();
        }
        if (!inSelection && (startX == -1) && (endX == -1)) {
            return false;
        }
        if ((startX != -1) && (endX != -1)) {
            if (endX < startX) {
                int temp = startX;
                startX = endX;
                endX = temp;
            }
        } else if ((startX != -1) && (endX == -1) && inSelection) {
            endX = startX;
            startX = -1;
        } else if ((startX == -1) && (endX != -1) && !inSelection) {
            startX = endX;
            endX = -1;
        }
        int index1 = -1;
        int index2 = -1;
        char[] wordChars = this.shownWord.toCharArray();
        if (startX != -1) {
            index1 = 0;
            FontMetrics fm = this.fontMetrics;
            for (int len = 0; len < wordChars.length; len++) {
                int w = fm.charsWidth(wordChars, 0, len);
                if (w > startX) {
                    break;
                }
                index1 = len;
            }
        }
        if (endX != -1) {
            index2 = 0;
            FontMetrics fm = this.fontMetrics;
            for (int len = 0; len < wordChars.length; len++) {
                int w = fm.charsWidth(wordChars, 0, len);
                if (w > endX) {
                    break;
                }
                index2 = len;
            }
        }
        if ((index1 != -1) || (index2 != -1)) {
            int startIndex = index1 == -1 ? 0 : index1;
            int endIndex = index2 == -1 ? wordChars.length : index2;
            buffer.append(wordChars, startIndex, endIndex - startIndex);
        } else {
            if (inSelection) {
                buffer.append(wordChars);
                return true;
            }
        }
        if ((index1 != -1) && (index2 != -1)) {
            return false;
        } else {
            return !inSelection;
        }
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#onMouseClick(java.awt.event
 * .MouseEvent, int, int)
 */
    @Override
    public boolean onMouseClick(MouseEvent event, int x, int y) {
        ModelNode me = this.modelNode;
        if (me != null) {
            return HtmlController.getInstance().onMouseClick(me, event, null,
                    x, y);
        } else {
            return true;
        }
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#onDoubleClick(java.awt.
 * event.MouseEvent, int, int)
 */
    @Override
    public boolean onDoubleClick(MouseEvent event, int x, int y) {
        ModelNode me = this.modelNode;
        if (me != null) {
            return HtmlController.getInstance().onDoubleClick(me, event, null,
                    x, y);
        } else {
            return true;
        }
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#onMousePressed(java.awt
 * .event.MouseEvent, int, int)
 */
    @Override
    public boolean onMousePressed(MouseEvent event, int x, int y) {
        ModelNode me = this.modelNode;
        if (me != null) {
            return HtmlController.getInstance().onMouseDown(me, event, null, x,
                    y);
        } else {
            return true;
        }
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#onMouseReleased(java.awt
 * .event.MouseEvent, int, int)
 */
    @Override
    public boolean onMouseReleased(MouseEvent event, int x, int y) {
        ModelNode me = this.modelNode;
        if (me != null) {
            return HtmlController.getInstance()
                    .onMouseUp(me, event, null, x, y);
        } else {
            return true;
        }
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#onMouseDisarmed(java.awt
 * .event.MouseEvent)
 */
    @Override
    public boolean onMouseDisarmed(MouseEvent event) {
        ModelNode me = this.modelNode;
        if (me != null) {
            return HtmlController.getInstance().onMouseDisarmed(me, event);
        } else {
            return true;
        }
    }

    @Override
    public boolean onKeyPressed(KeyEvent event) {

        ModelNode me = this.modelNode;
        if (me != null) {

            return HtmlController.getInstance().onKeyPress(this.modelNode,
                    event);

        } else {
            return true;
        }

    }

    @Override
    public boolean onKeyUp(KeyEvent event) {

        ModelNode me = this.modelNode;
        if (me != null) {

            return HtmlController.getInstance().onKeyUp(this.modelNode, event);

        } else {
            return true;
        }

    }

    @Override
    public boolean onKeyDown(KeyEvent event) {

        ModelNode me = this.modelNode;
        if (me != null) {

            return HtmlController.getInstance()
                    .onKeyDown(this.modelNode, event);

        } else {
            return true;
        }

    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#getLowestRenderableSpot
 * (int, int)
 */
    @Override
    public RenderableSpot getLowestRenderableSpot(int x, int y) {
        return new RenderableSpot(this, x, y);
    }

    /*
 * (non-Javadoc)
 * @see org.lobobrowser.html.renderer.BoundableRenderable#isContainedByNode()
 */
    @Override
    public boolean isContainedByNode() {
        return true;
    }

    /*
 * (non-Javadoc)
 * @see
 * org.lobobrowser.html.renderer.BoundableRenderable#onRightClick(java.awt.event
 * .MouseEvent, int, int)
 */
    @Override
    public boolean onRightClick(MouseEvent event, int x, int y) {
        ModelNode me = this.modelNode;
        if (me != null) {
            return HtmlController.getInstance().onContextMenu(me, event, x, y);
        } else {
            return true;
        }
    }

    /*
 * (non-Javadoc)
 * @see java.lang.Object#toString()
 */
    @Override
    public String toString() {
        return "RWord[word=" + this.shownWord + "]";
    }

	/**
	 * Gets the shown word.
	 *
	 * @return the shown word
	 */
	public String getShownWord() {
		return shownWord;
	}

	/**
	 * Sets the shown word.
	 *
	 * @param shownWord
	 *            the new shown word
	 */
	public void setShownWord(String shownWord) {
		this.shownWord = shownWord;
	}

	/**
	 * Gets the font metrics.
	 *
	 * @return the font metrics
	 */
	public FontMetrics getFontMetrics() {
		return fontMetrics;
	}

	/**
	 * Sets the font metrics.
	 *
	 * @param fontMetrics
	 *            the new font metrics
	 */
	public void setFontMetrics(FontMetrics fontMetrics) {
		this.fontMetrics = fontMetrics;
	}

	/**
	 * Gets the descent.
	 *
	 * @return the descent
	 */
	public int getDescent() {
		return descent;
	}

	/**
	 * Sets the descent.
	 *
	 * @param descent
	 *            the new descent
	 */
	public void setDescent(int descent) {
		this.descent = descent;
	}

	/**
	 * Gets the ascent plus leading.
	 *
	 * @return the ascent plus leading
	 */
	public int getAscentPlusLeading() {
		return ascentPlusLeading;
	}

	/**
	 * Sets the ascent plus leading.
	 *
	 * @param ascentPlusLeading
	 *            the new ascent plus leading
	 */
	public void setAscentPlusLeading(int ascentPlusLeading) {
		this.ascentPlusLeading = ascentPlusLeading;
	}

}
